跳到主要内容

无边框窗口如何实现拖拽、最大化和窗口控制?

考察点​

  • 无边框窗口的实现方式与平台差异
  • 拖拽区域、双击最大化等细节处理

参考答案​

1. 创建无边框窗口​

new BrowserWindow({
frame: false, // 去掉系统边框(Windows/Linux)
titleBarStyle: 'hiddenInset', // macOS:保留红绿灯按钮但隐藏标题栏
// macOS 还可用 trafficLightPosition 微调红绿灯位置
});

平台策略:

  • macOS:推荐 titleBarStyle: 'hidden' / 'hiddenInset',保留原生红绿灯(关闭/最小化/最大化),体验最原生
  • Windows/Linux:frame: false,三个控制按钮全部自己用 HTML 画

2. 拖拽区域​

/* 标题栏区域可拖拽 */
.titlebar {
-webkit-app-region: drag;
}

/* 标题栏上的按钮/输入框不可拖拽,否则点不到 */
.titlebar button,
.titlebar input {
-webkit-app-region: no-drag;
}

3. 自己实现窗口控制按钮(Windows)​

// 渲染进程通过桥接 API 调用
document.getElementById('min').onclick = () => window.api.window.min();
document.getElementById('max').onclick = () => window.api.window.toggleMax();
document.getElementById('close').onclick = () => window.api.window.close();

// 主进程实现
ipcMain.handle('window:toggle-max', (e) => {
const win = BrowserWindow.fromWebContents(e.sender);
win.isMaximized() ? win.unmaximize() : win.maximize();
});

同时要监听窗口状态同步按钮图标(最大化/还原切换):

win.on('maximize', () => win.webContents.send('window:max-changed', true));
win.on('unmaximize', () => win.webContents.send('window:max-changed', false));

4. 双击标题栏切换最大化​

app-region 的 drag 区域在 macOS 上双击自动跟随系统设置(最大化或最小化),Windows 上需要自己处理:

document.querySelector('.titlebar').addEventListener('dblclick', () => {
window.api.window.toggleMax(); // 仅 Windows 需要
});

5. 常见坑​

  1. Windows 最大化时遮挡任务栏/边框溢出:自绘边框在最大化时会超出屏幕几个像素,需在最大化态去掉外层 padding/border-radius。
  2. Windows Snap(拖标题栏贴边分屏):app-region drag 天然支持,但如果自己用 mouse 事件实现拖拽(不推荐)会丢失该能力。
  3. 可拖拽区域与滚动条冲突:drag 区域内的内容无法选中文本,注意交互边界。
  4. win.isMaximized() 在 Linux 部分桌面环境不可靠,需要降级方案。

面试话术​

macOS 用 hiddenInset 保留红绿灯最省事;Windows 全自绘,核心点是 drag/no-drag 的区域划分、三按钮走 IPC 调主进程 API、状态变化要回推渲染进程同步图标,另外要处理最大化时的边框溢出。

常见追问​

  • 能不能拖拽时实时改窗口位置? 可以用 mouse 事件 + win.setPosition,但会丢失系统原生拖拽体验(如 Windows Snap、macOS 空间切换),一般不推荐,除非要做异形窗口。
  • 圆角窗口怎么做? transparent: true + CSS border-radius,注意透明窗口在 Windows 上不能阴影,需 hasShadow 或自绘。